<template>
  <el-drawer v-bind="$attrs" @opened="onOpen" @close="onClose">
    <div style="height: 100%">
      <el-row style="height: 100%; overflow: auto">
        <el-col :md="24" :lg="12" class="left-editor">
          <div class="setting" title="资源引用" @click="showResource">
            <el-badge :is-dot="!!resources.length" class="item">
              <i class="el-icon-setting"></i>
            </el-badge>
          </div>
          <el-tabs v-model="activeTab" type="card" class="editor-tabs">
            <el-tab-pane name="html">
              <template #label>
                <span>
                  <i v-if="activeTab === 'html'" class="el-icon-edit"></i>
                  <i v-else class="el-icon-document"></i>
                  template
                </span>
              </template>
            </el-tab-pane>
            <el-tab-pane name="js">
              <template #label>
                <span>
                  <i v-if="activeTab === 'js'" class="el-icon-edit"></i>
                  <i v-else class="el-icon-document"></i>
                  script
                </span>
              </template>
            </el-tab-pane>
            <el-tab-pane name="css">
              <template #label>
                <span>
                  <i v-if="activeTab === 'css'" class="el-icon-edit"></i>
                  <i v-else class="el-icon-document"></i>
                  css
                </span>
              </template>
            </el-tab-pane>
          </el-tabs>
          <div
            v-show="activeTab === 'html'"
            id="editorHtml"
            class="tab-editor"
          ></div>
          <div
            v-show="activeTab === 'js'"
            id="editorJs"
            class="tab-editor"
          ></div>
          <div
            v-show="activeTab === 'css'"
            id="editorCss"
            class="tab-editor"
          ></div>
        </el-col>
        <el-col :md="24" :lg="12" class="right-preview">
          <div class="action-bar" :style="{ 'text-align': 'left' }">
            <span class="bar-btn" @click="runCode">
              <i class="el-icon-refresh"></i>
              刷新
            </span>
            <span class="bar-btn" @click="exportFile">
              <i class="el-icon-download"></i>
              导出vue文件
            </span>
            <span ref="copyBtn" @click="copyCode" class="bar-btn copy-btn">
              <i class="el-icon-document-copy"></i>
              复制代码
            </span>
            <span
              class="bar-btn delete-btn"
              @click="$emit('update:visible', false)"
            >
              <i class="el-icon-circle-close"></i>
              关闭
            </span>
          </div>
          <iframe
            v-show="isIframeLoaded"
            ref="previewPage"
            class="result-wrapper"
            frameborder="0"
            src="preview.html"
            @load="iframeLoad"
          ></iframe>
          <div
            v-show="!isIframeLoaded"
            v-loading="true"
            class="result-wrapper"
          ></div>
        </el-col>
      </el-row>
    </div>
  </el-drawer>
  <resource-dialog
    v-model:visible="resourceVisible"
    :origin-resource="resources"
    @save="setResource"
  />
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watch, computed } from "vue";
import { useClipboard } from "@vueuse/core";
import { saveAs } from "file-saver";
import { makeUpHtml, vueTemplate, vueScript, cssStyle } from "../settings/html";
import { makeUpJs } from "../settings/js";
import { makeUpCss } from "../settings/css";
import { exportDefault, beautifierConf, titleCase } from "../utils/common";
import ResourceDialog from "./ResourceDialog";
import beautifier from "js-beautify";
import * as monaco from "monaco-editor";

const editorObj = {
  html: null,
  js: null,
  css: null,
};
const mode = {
  html: "html",
  js: "javascript",
  css: "css",
};

const props = defineProps({
  formData: Object,
  generateConf: Object,
});

const { copy, isSupported } = useClipboard();
const activeTab = ref("html");
const htmlCode = ref("");
const jsCode = ref("");
const cssCode = ref("");
const codeFrame = ref("");
const isIframeLoaded = ref(false);
const isInitcode = ref(false);
const isRefreshCode = ref(false);
const resourceVisible = ref(false);
const scripts = ref([]);
const links = ref([]);
const previewPage = ref(null);

const resources = computed(() => scripts.value.concat(links.value));

const preventDefaultSave = (e) => {
  if (e.key === "s" && (e.metaKey || e.ctrlKey)) {
    e.preventDefault();
    copyCode();
  }
};

onMounted(() => {
  window.addEventListener("keydown", preventDefaultSave);
});

onBeforeUnmount(() => {
  window.removeEventListener("keydown", preventDefaultSave);
});

const copyCode = () => {
  if (!isSupported) {
    ElMessageBox.message({
      showClose: true,
      message: "浏览器不支持复制",
      type: "warning",
    });
    return;
  }
  const codeStr = generateCode();
  copy(codeStr);
  ElMessageBox.message({
    showClose: true,
    message: "代码已复制到剪切板，可粘贴。",
    type: "success",
  });
};

const onOpen = () => {
  const { type } = props.generateConf;
  htmlCode.value = makeUpHtml(props.formData, type);
  jsCode.value = makeUpJs(props.formData, type);
  cssCode.value = makeUpCss(props.formData);

  htmlCode.value = beautifier.html(htmlCode.value, beautifierConf.html);
  jsCode.value = beautifier.js(jsCode.value, beautifierConf.js);
  cssCode.value = beautifier.css(cssCode.value, beautifierConf.html);

  setEditorValue("editorHtml", "html", htmlCode.value);
  setEditorValue("editorJs", "js", jsCode.value);
  setEditorValue("editorCss", "css", cssCode.value);
  if (!isInitcode.value) {
    isRefreshCode.value = true;
    if (isIframeLoaded.value) {
      isInitcode.value = true;
      runCode();
    }
  }
};

const onClose = () => {
  isInitcode.value = false;
  isRefreshCode.value = false;
};

const iframeLoad = () => {
  if (!isInitcode.value) {
    isIframeLoaded.value = true;
    if (isRefreshCode.value) {
      isInitcode.value = true;
      runCode();
    }
  }
};
const setEditorValue = (id, type, codeStr) => {
  if (editorObj[type]) {
    editorObj[type].setValue(codeStr);
  } else {
    editorObj[type] = monaco.editor.create(document.getElementById(id), {
      value: codeStr,
      theme: "vs-dark",
      language: mode[type],
      automaticLayout: true,
    });
  }
  // ctrl + s 刷新
  editorObj[type].onKeyDown((e) => {
    if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
      runCode();
    }
  });
};

const runCode = () => {
  const jsCodeStr = editorObj.js.getValue();
  try {
    const ast = parse(jsCodeStr, { sourceType: "module" });
    const astBody = ast.program.body;
    if (astBody.length > 1) {
      ElMessageBox.confirm(
        "js格式不能识别，仅支持修改export default的对象内容",
        "提示",
        {
          type: "warning",
        }
      );
      return;
    }
    if (astBody[0].type === "ExportDefaultDeclaration") {
      const postData = {
        type: "refreshFrame",
        data: {
          generateConf: generateConf,
          html: editorObj.html.getValue(),
          js: jsCodeStr.replace(exportDefault, ""),
          css: editorObj.css.getValue(),
          scripts: scripts,
          links: links,
        },
      };

      previewPage.value.contentWindow.postMessage(postData, location.origin);
    } else {
      ElMessageBox.message.error("请使用export default");
    }
  } catch (err) {
    ElMessageBox.message.error(`js错误：${err}`);
    console.error(err);
  }
};

const generateCode = () => {
  const html = vueTemplate(editorObj.html.getValue());
  const script = vueScript(editorObj.js.getValue());
  const css = cssStyle(editorObj.css.getValue());
  return beautifier.html(html + script + css, beautifierConf.html);
};

const exportFile = () => {
  this.$prompt("文件名:", "导出文件", {
    inputValue: `${+new Date()}.vue`,
    closeOnClickModal: false,
    inputPlaceholder: "请输入文件名",
  }).then(({ value }) => {
    if (!value) value = `${+new Date()}.vue`;
    const codeStr = this.generateCode();
    const blob = new Blob([codeStr], { type: "text/plain;charset=utf-8" });
    saveAs(blob, value);
  });
};

const showResource = () => {
  resourceVisible.value = true;
};

const setResource = (arr) => {
  const scripts = [];
  const links = [];
  if (Array.isArray(arr)) {
    arr.forEach((item) => {
      if (item.endsWith(".css")) {
        links.push(item);
      } else {
        scripts.push(item);
      }
    });
    this.scripts = scripts;
    this.links = links;
  } else {
    this.scripts = [];
    this.links = [];
  }
};
</script>

<style lang="scss" scoped>
@use "../mixin.scss" as *;

.tab-editor {
  position: absolute;
  top: 33px;
  bottom: 0;
  left: 0;
  right: 0;
  font-size: 14px;
}
.left-editor {
  position: relative;
  height: 100%;
  background: #1e1e1e;
  overflow: hidden;
}
.setting {
  position: absolute;
  right: 15px;
  top: 3px;
  color: #a9f122;
  font-size: 18px;
  cursor: pointer;
  z-index: 1;
}
.right-preview {
  height: 100%;
  .result-wrapper {
    height: calc(100vh - 33px);
    width: 100%;
    overflow: auto;
    padding: 12px;
    box-sizing: border-box;
  }
}
@include action-bar;
:deep(.el-drawer__header) {
  display: none;
}
</style>
